/*
 * File: chipid.c
 *
 * Chip-ID parametres
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <stm32.h>
#include <stlink.h>
#include "chipid.h"

#include "logging.h"

// #include <ctype.h> // TODO: Check use
// #include <errno.h> // TODO: Check use

static struct stlink_chipid_params *devicelist;

struct stlink_chipid_params devicelist_const[] = {
    {"unknown"           , NULL, 0x0  , 0 , 0x0       , 0x0    , 0x0     , 0x0       , 0x0    , 0x0       , 0x0 , 0 , 0x0, 0x0},
    {"STM32WLEx"         , NULL, 0x497, 12, 0x1fff75e0, 0x800  , 0x10000 , 0x1fff0000, 0x7000 , 0x1fff7800, 0x10, 3 , 0x0, 0x0},
    {"STM32WBx0_WBx5"    , NULL, 0x495, 12, 0x1fff75e0, 0x1000 , 0x40000 , 0x1fff0000, 0x7000 , 0x1fff8000, 0x80, 2 , 0x0, 0x0},
    {"STM32U59x_U5Ax"    , NULL, 0x481, 11, 0xbfa07a0 , 0x2000 , 0x274800, 0xbf90000 , 0x8000 , 0x0       , 0x0 , 3 , 0x0, 0x0},
    {"STM32U575_U585"    , NULL, 0x482, 11, 0xbfa07a0 , 0x2000 , 0xc4800 , 0xbf90000 , 0x10000, 0x0       , 0x0 , 3 , 0x0, 0x0},
    {"STM32U5Fx_U5Gx"    , NULL, 0x476, 11, 0xbfa07a0 , 0x2000 , 0x2f4800, 0xbf90000 , 0x8000 , 0x0       , 0x0 , 3 , 0x0, 0x0},
    {"STM32U535_U545"    , NULL, 0x455, 11, 0xbfa07a0 , 0x2000 , 0x44800 , 0xbf90000 , 0x8000 , 0x0       , 0x0 , 3 , 0x0, 0x0},
    {"STM32L5x2xx"       , NULL, 0x472, 11, 0xbfa05e0 , 0x1000 , 0x40000 , 0xbf90000 , 0x8000 , 0x0       , 0x0 , 13, 0x0, 0x0},
    {"STM32L4Rx"         , NULL, 0x470, 10, 0x1fff75e0, 0x1000 , 0xa0000 , 0x1fff0000, 0x7000 , 0x1ff00000, 0x4 , 2 , 0x0, 0x0},
    {"STM32L4Px_L4Qx"    , NULL, 0x471, 10, 0x1fff75e0, 0x1000 , 0xa0000 , 0x1fff0000, 0x7000 , 0x1ff00000, 0x4 , 2 , 0x0, 0x0},
    {"STM32L47x_L48x"    , NULL, 0x415, 10, 0x1fff75e0, 0x800  , 0x18000 , 0x1fff0000, 0x7000 , 0x1fff7800, 0x4 , 2 , 0x0, 0x0},
    {"STM32L41x_L42x"    , NULL, 0x435, 10, 0x1fff75e0, 0x800  , 0xc000  , 0x1fff0000, 0x7000 , 0x1fff7800, 0x4 , 2 , 0x0, 0x0},
    {"STM32L41x_L42x"    , NULL, 0x464, 10, 0x1fff75e0, 0x800  , 0xa000  , 0x1fff0000, 0x7000 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32L1xx_Cat_5"   , NULL, 0x437, 9 , 0x1ff800cc, 0x100  , 0x14000 , 0x1ff00000, 0x1000 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32L1xx_Cat_4"   , NULL, 0x436, 9 , 0x1ff800cc, 0x100  , 0xc000  , 0x1ff00000, 0x1000 , 0x1ff80000, 0x8 , 2 , 0x0, 0x0},
    {"STM32L1xx_Cat_3"   , NULL, 0x427, 9 , 0x1ff800cc, 0x100  , 0x8000  , 0x1ff00000, 0x1000 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32L1xx_Cat_2"   , NULL, 0x429, 9 , 0x1ff8004c, 0x100  , 0x8000  , 0x1ff00000, 0x1000 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32L1xx_Cat_1"   , NULL, 0x416, 9 , 0x1ff8004c, 0x100  , 0x4000  , 0x1ff00000, 0x1000 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32L0xxx_Cat_5"  , NULL, 0x447, 9 , 0x1ff8007c, 0x80   , 0x5000  , 0x1ff00000, 0x2000 , 0x1ff80000, 0x20, 13, 0x0, 0x0},
    {"STM32L0xxx_Cat_3"  , NULL, 0x417, 9 , 0x1ff8007c, 0x80   , 0x2000  , 0x1ff00000, 0x1000 , 0x1ff80000, 0x20, 0 , 0x0, 0x0},
    {"STM32L0xxx_Cat_2"  , NULL, 0x425, 9 , 0x1ff8007c, 0x80   , 0x2000  , 0x1ff00000, 0x1000 , 0x1ff80000, 0x20, 0 , 0x0, 0x0},
    {"STM32L0xxx_Cat_1"  , NULL, 0x457, 9 , 0x1ff8007c, 0x80   , 0x2000  , 0x1ff00000, 0x2000 , 0x1ff80000, 0x20, 0 , 0x0, 0x0},
    {"STM32H7Ax_H7Bx"    , NULL, 0x480, 8 , 0x8fff80c , 0x2000 , 0x20000 , 0x1ff00000, 0x20000, 0x5200201c, 0x2c, 3 , 0x0, 0x0},
    {"STM32H74x_H75x"    , NULL, 0x450, 8 , 0x1ff1e880, 0x20000, 0x20000 , 0x1ff00000, 0x20000, 0x5200201c, 0x2c, 3 , 0x0, 0x0},
    {"STM32H72x_H73x"    , NULL, 0x483, 8 , 0x1ff1e880, 0x20000, 0x20000 , 0x1ff00000, 0x20000, 0x5200201c, 0x2c, 2 , 0x0, 0x0},
    {"STM32H5xx"         , NULL, 0x484, 11, 0x8fff80c , 0x2000 , 0xa0000 , 0xbf80000 , 0x8000 , 0x0       , 0x0 , 13, 0x0, 0x0},
    {"STM32G49x_G4Ax"    , NULL, 0x479, 7 , 0x1fff75e0, 0x800  , 0x1c000 , 0x1fff0000, 0x7000 , 0x1ffff800, 0x4 , 3 , 0x0, 0x0},
    {"STM32G47x_G48x"    , NULL, 0x469, 7 , 0x1fff75e0, 0x800  , 0x20000 , 0x1fff0000, 0x7000 , 0x1ffff800, 0x4 , 3 , 0x0, 0x0},
    {"STM32G43x_G44x"    , NULL, 0x468, 7 , 0x1fff75e0, 0x800  , 0x8000  , 0x1fff0000, 0x7000 , 0x1ffff800, 0x4 , 2 , 0x0, 0x0},
    {"STM32G0Bx_G0Cx"    , NULL, 0x467, 6 , 0x1fff75e0, 0x800  , 0x24000 , 0x1fff0000, 0x7000 , 0x1fff7800, 0x80, 13, 0x0, 0x0},
    {"STM32G07x_G08x"    , NULL, 0x460, 6 , 0x1fff75e0, 0x800  , 0x9000  , 0x1fff0000, 0x7000 , 0x1fff7800, 0x80, 0 , 0x0, 0x0},
    {"STM32G05x_G06x"    , NULL, 0x456, 6 , 0x1fff75e0, 0x800  , 0x4800  , 0x1fff0000, 0x7000 , 0x1fff7800, 0x80, 0 , 0x0, 0x0},
    {"STM32G03x_G04x"    , NULL, 0x466, 6 , 0x1fff75e0, 0x800  , 0x2000  , 0x1fff0000, 0x2000 , 0x1fff7800, 0x80, 0 , 0x0, 0x0},
    {"STM32F76x_F77x"    , NULL, 0x451, 5 , 0x1ff0f442, 0x800  , 0x80000 , 0x200000  , 0xedc0 , 0x1fff0000, 0x20, 3 , 0x0, 0x0},
    {"STM32F74x_F75x"    , NULL, 0x449, 5 , 0x1ff0f442, 0x800  , 0x50000 , 0x100000  , 0xedc0 , 0x1fff0000, 0x20, 2 , 0x0, 0x0},
    {"STM32F72x_F73x"    , NULL, 0x452, 5 , 0x1ff07a22, 0x800  , 0x40000 , 0x100000  , 0xedc0 , 0x1fff0000, 0x20, 2 , 0x0, 0x0},
    {"STM32F4x5_F4x7"    , NULL, 0x413, 4 , 0x1fff7a22, 0x4000 , 0x30000 , 0x1fff0000, 0x7800 , 0x40023c14, 0x4 , 2 , 0x0, 0x0},
    {"STM32F46x_F47x"    , NULL, 0x434, 4 , 0x1fff7a22, 0x4000 , 0x40000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F446"         , NULL, 0x421, 4 , 0x1fff7a22, 0x20000, 0x20000 , 0x1fff0000, 0x7800 , 0x40023c14, 0x10, 2 , 0x0, 0x0},
    {"STM32F42x_F43x"    , NULL, 0x419, 4 , 0x1fff7a22, 0x4000 , 0x40000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F413_F423"    , NULL, 0x463, 4 , 0x1fff7a22, 0x4000 , 0x50000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F412"         , NULL, 0x441, 4 , 0x1fff7a22, 0x4000 , 0x40000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F411xC_xE"    , NULL, 0x431, 4 , 0x1fff7a22, 0x4000 , 0x20000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F410"         , NULL, 0x458, 4 , 0x1fff7a22, 0x4000 , 0x8000  , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F401xD_xE"    , NULL, 0x433, 4 , 0x1fff7a22, 0x4000 , 0x18000 , 0x1fff0000, 0x7800 , 0x40023c14, 0x4 , 2 , 0x0, 0x0},
    {"STM32F401xB_xC"    , NULL, 0x423, 4 , 0x1fff7a22, 0x4000 , 0x10000 , 0x1fff0000, 0x7800 , 0x0       , 0x0 , 2 , 0x0, 0x0},
    {"STM32F37x"         , NULL, 0x432, 2 , 0x1ffff7cc, 0x800  , 0xa000  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F2xx"         , NULL, 0x411, 4 , 0x1fff7a22, 0x20000, 0x20000 , 0x1fff0000, 0x7800 , 0x1fffc000, 0x4 , 2 , 0x0, 0x0},
    {"STM32F1xx_XLD"     , NULL, 0x430, 3 , 0x1ffff7e0, 0x800  , 0x18000 , 0x1fffe000, 0x1800 , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F1xx_VL_MD_LD", NULL, 0x420, 2 , 0x1ffff7e0, 0x400  , 0x2000  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F1xx_VL_HD"   , NULL, 0x428, 2 , 0x1ffff7e0, 0x800  , 0x8000  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F1xx_MD"      , NULL, 0x410, 2 , 0x1ffff7e0, 0x400  , 0x5000  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F1xx_LD"      , NULL, 0x412, 2 , 0x1ffff7e0, 0x400  , 0x2800  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"F1xx_HD"           , NULL, 0x414, 2 , 0x1ffff7e0, 0x800  , 0x10000 , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F1xx_CL"      , NULL, 0x418, 2 , 0x1ffff7e0, 0x800  , 0x10000 , 0x1fffb000, 0x4800 , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F09x"         , NULL, 0x442, 2 , 0x1ffff7cc, 0x800  , 0x8000  , 0x1fffd800, 0x2000 , 0x1ffff800, 0x10, 0 , 0x0, 0x0},
    {"STM32F07x"         , NULL, 0x448, 2 , 0x1ffff7cc, 0x800  , 0x4000  , 0x1fffc800, 0x3000 , 0x1ffff800, 0x10, 0 , 0x0, 0x0},
    {"STM32F05x"         , NULL, 0x440, 2 , 0x1ffff7cc, 0x400  , 0x2000  , 0x1fffec00, 0xc00  , 0x1ffff800, 0x10, 0 , 0x0, 0x0},
    {"STM32F04x"         , NULL, 0x445, 2 , 0x1ffff7cc, 0x400  , 0x1800  , 0x1fffec00, 0xc00  , 0x1ffff800, 0x10, 0 , 0x0, 0x0},
    {"STM32F03x"         , NULL, 0x444, 2 , 0x1ffff7cc, 0x400  , 0x1000  , 0x1fffec00, 0xc00  , 0x1ffff800, 0x10, 0 , 0x0, 0x0},
    {"STM32C031xx"       , NULL, 0x453, 1 , 0x1fff75a0, 0x800  , 0x3000  , 0x1fff0000, 0x1800 , 0x1fff7800, 0x80, 0 , 0x0, 0x0},
    {"STM32C011xx"       , NULL, 0x443, 1 , 0x1fff75a0, 0x800  , 0x1800  , 0x1fff0000, 0x1800 , 0x1fff7800, 0x80, 0 , 0x0, 0x0},
    {"STM32F303_F328_F334"   , NULL, 0x438, 2 , 0x1ffff7cc, 0x800  , 0x3000  , 0x1fffd800, 0x2000 , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F302_F303_F398_HD", NULL, 0x446, 2 , 0x1ffff7cc, 0x800  , 0x10000 , 0x1fffd800, 0x2000 , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F302_F303_358"    , NULL, 0x422, 2 , 0x1ffff7cc, 0x800  , 0xa000  , 0x1ffff000, 0x800  , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32F301_F302_F318"   , NULL, 0x439, 2 , 0x1ffff7cc, 0x800  , 0xa000  , 0x1fffd800, 0x2000 , 0x1ffff800, 0x10, 2 , 0x0, 0x0},
    {"STM32L496x_L4A6x"      , NULL, 0x461, 10, 0x1fff75e0, 0x800  , 0x50000 , 0x1fff0000, 0x7000 , 0x1fff7800, 0x4 , 2 , 0x1fff7000, 0x400},
    {"STM32L45x_L46x"        , NULL, 0x462, 10, 0x1fff75e0, 0x800  , 0x20000 , 0x1fff0000, 0x7000 , 0x0       , 0x0 , 2 , 0x1fff7000, 0x400},
    {NULL}
};

void stlink_print_devicelist() {
  struct stlink_chipid_params *dev = NULL;
  printf("{");
  for (dev = devicelist; dev != NULL; dev = dev->next) {
    printf("\n");
    printf("{");
    printf("\"%s\","   , dev->dev_type);
    printf("NULL,");
    printf("0x%x," , dev->chip_id);
    printf("%d,"   , dev->flash_type);
    printf("0x%x," , dev->flash_size_reg);
    printf("0x%x," , dev->flash_pagesize);
    printf("0x%x," , dev->sram_size);
    printf("0x%x," , dev->bootrom_base);
    printf("0x%x," , dev->bootrom_size);
    printf("0x%x," , dev->option_base);
    printf("0x%x," , dev->option_size);
    printf("%d,"   , dev->flags);
    printf("0x%x," , dev->otp_base);
    printf("0x%x"  , dev->otp_size);
    printf("},");
  }
  printf("{NULL}");
  printf("\n}");
}

void stlink_add_devicelist(struct stlink_chipid_params arr[]) {
  int i = 0;
  for(i=0; arr[i].dev_type != NULL; i++) {
    struct stlink_chipid_params *ts;
    ts = calloc(1, sizeof(struct stlink_chipid_params));
    if (ts==NULL) {
        continue;
    }
    ts->dev_type = arr[i].dev_type;
    ts->ref_manual_id = arr[i].ref_manual_id;
    ts->chip_id = arr[i].chip_id;
    ts->flash_type = arr[i].flash_type;
    ts->flash_size_reg = arr[i].flash_size_reg;
    ts->flash_pagesize = arr[i].flash_pagesize;
    ts->sram_size = arr[i].sram_size;
    ts->bootrom_base = arr[i].bootrom_base;
    ts->bootrom_size = arr[i].bootrom_size;
    ts->option_base = arr[i].option_base;
    ts->option_size = arr[i].option_size;
    ts->flags = arr[i].flags;
    ts->otp_base = arr[i].otp_base;
    ts->otp_size = arr[i].otp_size;
    ts->next = devicelist;
    devicelist = ts;
  }
}

void stlink_auto_devicelist() {
    stlink_add_devicelist(devicelist_const);
}

void dump_a_chip(struct stlink_chipid_params *dev) {
  DLOG("# Device Type: %s\n", dev->dev_type);
  DLOG("# Reference Manual: RM%s\n", dev->ref_manual_id);
  DLOG("#\n");
  DLOG("chip_id 0x%x\n", dev->chip_id);
  DLOG("flash_type %d\n", dev->flash_type);
  DLOG("flash_size_reg 0x%x\n", dev->flash_size_reg);
  DLOG("flash_pagesize 0x%x\n", dev->flash_pagesize);
  DLOG("sram_size 0x%x\n", dev->sram_size);
  DLOG("bootrom_base 0x%x\n", dev->bootrom_base);
  DLOG("bootrom_size 0x%x\n", dev->bootrom_size);
  DLOG("option_base 0x%x\n", dev->option_base);
  DLOG("option_size 0x%x\n", dev->option_size);
  DLOG("flags %d\n\n", dev->flags);
  DLOG("otp_base %d\n\n", dev->otp_base);
  DLOG("otp_size %d\n\n", dev->otp_size);
}

struct stlink_chipid_params *stlink_chipid_get_params(uint32_t chip_id) {
  struct stlink_chipid_params *params = NULL;
  for (params = devicelist; params != NULL; params = params->next)
    if (params->chip_id == chip_id) {
      DLOG("detected chip_id parameters\n\n");
      dump_a_chip(params);
      break;
    }

  return (params);
}

void process_chipfile(char *fname) {
  FILE *fp;
  char *p, buf[256];
  char word[64], value[64];
  struct stlink_chipid_params *ts;
  int32_t nc;

  // fprintf (stderr, "processing chip-id file %s.\n", fname);
  fp = fopen(fname, "r");

  if (!fp) {
    perror(fname);
    return;
  }

  ts = calloc(1, sizeof(struct stlink_chipid_params));

  while (fgets(buf, sizeof(buf), fp) != NULL) {

    if (strncmp(buf, "#", strlen("#")) == 0)
      continue; // ignore comments

    if ((strncmp(buf, "\n", strlen("\n")) == 0) ||
        (strncmp(buf, " ", strlen(" ")) == 0))
      continue; // ignore empty lines

    if (sscanf(buf, "%63s %63s", word, value) != 2) {
      fprintf(stderr, "Failed to read keyword or value\n");
      continue;
    }

    if (strcmp(word, "dev_type") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      ts->dev_type = strdup(buf + nc);
    } else if (strcmp(word, "ref_manual_id") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      ts->ref_manual_id = strdup(buf + nc);
    } else if (strcmp(word, "chip_id") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->chip_id) < 1) {
        fprintf(stderr, "Failed to parse chip-id\n");
      }
    } else if (strcmp(word, "flash_type") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      // Match human readable flash_type with enum stm32_flash_type { }.
      if(strcmp(value, "C0") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_C0;
      } else if (strcmp(value, "F0_F1_F3") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_F0_F1_F3;
      } else if (strcmp(value, "F1_XL") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_F1_XL;
      } else if (strcmp(value, "F2_F4") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_F2_F4;
      } else if (strcmp(value, "F7") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_F7;
      } else if (strcmp(value, "G0") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_G0;
      } else if (strcmp(value, "G4") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_G4;
      } else if (strcmp(value, "H7") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_H7;
      } else if (strcmp(value, "L0_L1") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_L0_L1;
      } else if (strcmp(value, "L4") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_L4;
      } else if (strcmp(value, "L5_U5_H5") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_L5_U5_H5;
      } else if (strcmp(value, "WB_WL") == 0) {
        ts->flash_type = STM32_FLASH_TYPE_WB_WL;
      } else {
        ts->flash_type = STM32_FLASH_TYPE_UNKNOWN;
      }
    } else if (strcmp(word, "flash_size_reg") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->flash_size_reg) < 1) {
        fprintf(stderr, "Failed to parse flash size reg\n");
      }
    } else if (strcmp(word, "flash_pagesize") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->flash_pagesize) < 1) {
        fprintf(stderr, "Failed to parse flash page size\n");
      }
    } else if (strcmp(word, "sram_size") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->sram_size) < 1) {
        fprintf(stderr, "Failed to parse SRAM size\n");
      }
    } else if (strcmp(word, "bootrom_base") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->bootrom_base) < 1) {
        fprintf(stderr, "Failed to parse BootROM base\n");
      }
    } else if (strcmp(word, "bootrom_size") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->bootrom_size) < 1) {
        fprintf(stderr, "Failed to parse BootROM size\n");
      }
    } else if (strcmp(word, "option_base") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->option_base) < 1) {
        fprintf(stderr, "Failed to parse option base\n");
      }
    } else if (strcmp(word, "option_size") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->option_size) < 1) {
        fprintf(stderr, "Failed to parse option size\n");
      }
    } else if (strcmp(word, "flags") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      p = strtok(buf, " \t\n");

      while ((p = strtok(NULL, " \t\n"))) {
        if (strcmp(p, "none") == 0) {
          // NOP
        } else if (strcmp(p, "dualbank") == 0) {
          ts->flags |= CHIP_F_HAS_DUAL_BANK;
        } else if (strcmp(p, "swo") == 0) {
          ts->flags |= CHIP_F_HAS_SWO_TRACING;
        } else {
          fprintf(stderr, "Unknown flags word in %s: '%s'\n", fname, p);
        }
      }

      sscanf(value, "%x", &ts->flags);
    } else if (strcmp(word, "otp_base") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->otp_base) < 1) {
        fprintf(stderr, "Failed to parse option size\n");
      }
    } else if (strcmp(word, "otp_size") == 0) {
      buf[strlen(buf) - 1] = 0; // chomp newline
      sscanf(buf, "%*s %n", &nc);
      if (sscanf(value, "%i", &ts->otp_size) < 1) {
        fprintf(stderr, "Failed to parse option size\n");
      }
    } else {
      fprintf(stderr, "Unknown keyword in %s: %s\n", fname, word);
    }
  }
  fclose(fp);
  ts->next = devicelist;
  devicelist = ts;
}

#if defined(STLINK_HAVE_DIRENT_H)
#include <dirent.h>

void init_chipids(char *dir_to_scan) {
  DIR *d;
  uint32_t nl; // namelen
  struct dirent *dir;

  if (!dir_to_scan) {
    dir_to_scan = "./";
  }

  devicelist = NULL;
  d = opendir(dir_to_scan);

  if (d) {
    while ((dir = readdir(d)) != NULL) {
      nl = strlen(dir->d_name);

      if (strcmp(dir->d_name + nl - 5, ".chip") == 0) {
        char buf[1024];
        sprintf(buf, "%s/%s", dir_to_scan, dir->d_name);
        process_chipfile(buf);
      }
    }

    closedir(d);
  } else {
    perror(dir_to_scan);
    return;
  }
}

#endif // STLINK_HAVE_DIRENT_H

#if defined(_WIN32) && !defined(STLINK_HAVE_DIRENT_H)
#if !defined(WINXP)
#include <fileapi.h>
#endif
#include <strsafe.h>

void init_chipids(char *dir_to_scan) {
  HANDLE hFind = INVALID_HANDLE_VALUE;
  WIN32_FIND_DATAA ffd;
  char filepath[MAX_PATH] = {0};
  StringCchCopyA(filepath, STLINK_ARRAY_SIZE(filepath), dir_to_scan);

  if (FAILED(
          StringCchCatA(filepath, STLINK_ARRAY_SIZE(filepath), "\\*.chip"))) {
    ELOG("Path to chips's dir too long.\n");
    return;
  }

  hFind = FindFirstFileA(filepath, &ffd);

  if (INVALID_HANDLE_VALUE == hFind) {
    ELOG("Can't find any chip description file in %s.\n", filepath);
    return;
  }

  do {
    memset(filepath, 0, STLINK_ARRAY_SIZE(filepath));
    StringCchCopyA(filepath, STLINK_ARRAY_SIZE(filepath), dir_to_scan);
    StringCchCatA(filepath, STLINK_ARRAY_SIZE(filepath), "\\");
    StringCchCatA(filepath, STLINK_ARRAY_SIZE(filepath), ffd.cFileName);
    process_chipfile(filepath);
  } while (FindNextFileA(hFind, &ffd) != 0);

  FindClose(hFind);
}

#endif // defined(_WIN32) && !defined(STLINK_HAVE_DIRENT_H)
